#pragma once
#include "Log.hpp"
#include "Util.hpp"
#include <ctime>
#include <queue>
#include <unordered_map>
#include <mysql/mysql.h>
#include <random>
#include <sstream>
#include <iomanip> // 包含 std::hex

namespace ns_usr
{
    // MYSQL配置
#define USER "oj_usr"
#define HOST "127.0.0.1"
#define PASSWORD "123456789"
#define DB "MyOJUsers"
#define PORT 3306

    using namespace ns_log;
    using namespace ns_util;
    // session类
    class Session
    {
    public:
        Session()
        {
            begin = time(nullptr);
            end = begin + haveTime;
            // 创建一个随机数生成器
            std::random_device rd;
            // 使用 Mersenne Twister 算法生成随机数
            std::mt19937 gen(rd());
            // 创建一个均匀分布的随机数生成器，范围是 0 到 15
            std::uniform_int_distribution<> dis(0, 15);
            // 创建一个字符串流对象，用于拼接生成的会话 ID
            std::stringstream ss;
            // 生成 32 个随机字符
            for (int i = 0; i < 32; ++i)
            {
                // 将生成的随机数转换为十六进制格式，并添加到字符串流中
                ss << std::hex << dis(gen);
            }
            sessionId = ss.str();
        }
        std::string sessionId;     // 标识
        std::time_t begin;         // 创建时间
        std::time_t end;           // 结束时间
        std::time_t haveTime = 3600; // 有效时间：1h
    };
    // 消息类
    class Message
    {
    public:
        Message() {}
        Message(std::string name_, std::string text_, time_t sendtime_)
            : name(name_), text(text_), sendtime(sendtime_)
        {
        }

        std::string name; // 发消息的人
        std::string text; // 消息内容
        time_t sendtime;  // 发送时间
    };
    // 用户类
    class User
    {
    public:
        User()
        {
        }
        User(std::string idname_, std::string password_, std::string email_)
            : idname(idname_), password(password_), email(email_)
        {
        }
        std::string idname;
        std::string password;
        std::string email;
    };
    // 数据库链接管理
    class UseMysql
    {
    private:
        std::string _user;
        std::string _host;
        std::string _password;
        std::string _db;
        size_t _port;
        MYSQL *_my;
        bool MysqlConnect()
        {
            // LOG(DEBUG) << _user << " " << _host << " " << _password << " " << _db << " " << _port << "\n";
            if (mysql_real_connect(_my, _host.c_str(), _user.c_str(), _password.c_str(), _db.c_str(), _port, nullptr, 0) == nullptr)
            {
                LOG(FAIL) << "数据库链接失败" << "\n";
                return false;
            }
            return true;
        }

    public:
        UseMysql(std::string user = USER, std::string host = HOST, std::string password = PASSWORD, std::string db = DB, size_t port = PORT)
            : _user(user), _host(host), _password(password), _db(db), _port(port)
        {
            _my = mysql_init(nullptr);
            MysqlConnect();
        }
        // value格式："name,password,email"
        bool InsertSql(const std::string &value)
        {
            std::string sql = "insert into Users values (";
            sql += value;
            sql += ");";
            LOG(DEBUG) << "SQL:" << sql << "\n";
            int ret = mysql_query(_my, sql.c_str());
            if (ret != 0)
            {
                // 插入失败
                LOG(DEBUG) << "查询出错,用户名已经存在或sql有误" << "\n";
                return false;
            }
            return true;
        }
        /******************************
         * 这是不带筛选的查询
         * ret: 输出型参数,查询结果
         * needStr: 查询时需要的保存的选项,默认未"*"全需要
         *****************************/
        bool SelectAllSql(std::vector<std::vector<std::string>> *ret, const std::string &needStr = "*")
        {
            std::string sql = "select ";
            sql += needStr;
            sql += " from Users";
            // LOG(DEBUG) << "SQL:" << sql << "\n";
            MYSQL_RES *res;
            MYSQL_ROW row;
            int retcode = mysql_query(_my, sql.c_str());
            if (retcode != 0)
            {
                // 查询出错
                LOG(DEBUG) << "sql有误" << "\n";
                return false;
            }
            res = mysql_store_result(_my);
            if (!res)
            {
                LOG(DEBUG) << "res is null" << "\n";
                return false;
            }
            int num_fields = mysql_num_fields(res);
            if (num_fields == 0)
            {
                // 数据库被删空
                return false;
            }
            while (row = mysql_fetch_row(res))
            {
                if (!row)
                {
                    // 没有数据行
                    LOG(DEBUG) << "改行没有数据行" << "\n";
                    continue;
                }
                std::vector<std::string> tmp;
                // 将结果返回
                for (int i = 0; i < num_fields; ++i)
                    tmp.push_back(row[i]);
                ret->push_back(tmp);
            }
            mysql_free_result(res);
            return true;
        }
        /******************************
         * ret: 输出型参数,查询结果
         * key格式：idname
         * needStr: 查询时需要的保存的选项,默认未"*"全需要
         *****************************/
        bool SelectHaveKeySql(const std::string &key, std::vector<std::string> *ret, const std::string &needStr = "*")
        {
            std::string sql = "select ";
            sql += needStr;
            sql += " from Users where idname = '";
            sql += key;
            sql += "';";
            // LOG(DEBUG) << "SQL:" << sql << "\n";
            MYSQL_RES *res;
            MYSQL_ROW row;
            int retcode = mysql_query(_my, sql.c_str());
            if (retcode != 0)
            {
                // 查询出错
                LOG(DEBUG) << "查询出错,用户名不存在或sql有误" << "\n";
                return false;
            }
            res = mysql_store_result(_my);

            if (!res)
            {
                LOG(DEBUG) << "res is null" << "\n";
                return false;
            }
            int num_fields = mysql_num_fields(res);
            // LOG(DEBUG) << num_fields << "\n";

            if (num_fields == 0)
            {
                // 查询结果无数据
                LOG(DEBUG) << "查询结果无数据" << "\n";
                return false;
            }
            row = mysql_fetch_row(res);
            if (!row)
            {
                // 没有数据行
                LOG(DEBUG) << "没有数据行" << "\n";
                mysql_free_result(res);
                return false;
            }
            // 将结果返回
            for (int i = 0; i < num_fields; ++i)
                ret->push_back(row[i]);
            mysql_free_result(res);
            return true;
        }
        ~UseMysql()
        {
            mysql_close(_my);
        }
    };
    //给下面的优先级队列使用
    class Compare
    {
    public:
        bool operator()(const Message &m1, const Message &m2)
        {
            return m1.sendtime > m2.sendtime;
        }
    };
    // 用户管理
    class ManageUser
    {
    public:
    private:
    // 用户消息管理
        typedef std::priority_queue<Message, std::vector<Message>, Compare> pri_queue;
        // 存储所有消息
        std::vector<Message> allMessages;
        // 通过用户唯一键值找存储的消息队列
        std::unordered_map<std::string, pri_queue> id_messages;
    
    // 用户session管理
        //  sessionId映射用户
        std::unordered_map<std::string, User> seid_usr;
        // 管理session,
        std::unordered_map<std::string, Session> seid_sess;
        // 管理用户
        std::unordered_map<std::string, User> id_user;
        // 管理数据库
        UseMysql _manage_sql;
        //
        // 单例指针
        static ManageUser *_manUsr;

    private:
    public:
        ManageUser()
        {
            LoginUsers();
        }
        ~ManageUser()
        {
        }
        // 加载用户到内存
        bool LoginUsers()
        {
            std::vector<std::vector<std::string>> ret;
            _manage_sql.SelectAllSql(&ret);
            if (ret.size() == 0)
            {
                // LOG(DEBUG) << "数据库无用户信息" << "\n";
                return false;
            }
            for (int i = 0; i < ret.size(); ++i)
            {
                std::string idname = ret[i][0];
                User usr;
                usr.idname = idname;
                usr.password = ret[i][1];
                usr.email = ret[i][2];
                LOG(DEBUG) << "idname:" << usr.idname << " password:" << usr.password << " email:" << usr.email << "\n";
                id_user[idname] = usr;
                // 加载消息队列
                id_messages[idname] = pri_queue();
            }
            return true;
        }

        /**************************************
         * 通过idname给一条消息队列的消息
         * 参数：    
         *  idname:需要哪个用户的消息队列
         *  message:输入输出型参数，返回得到的消息
         * 返回值：
         * true:成功返回
         * false:队列为空
         *************************************/
        bool UserGetOneMessage(const std::string &idname,Message &m)
        {
            auto it = id_messages.find(idname);
            if(it == id_messages.end())
            {
                return false;
            }            
            if((it->second).empty())
                return false;
            m = (it->second).top();
            (it->second).pop();
            return true;
        }

        //传入发送消息的用户，以及消息内容
        bool HaveNewMessage(const std::string &idname,const Message &m)
        {
            //保存到所有消息中
            allMessages.push_back(m);
            //保存至用户消息队列中
            for(auto &it : id_messages)
            {
                if(it.first != idname)
                    it.second.push(m);
            }
            return true;   
        }

        // 使用sessionId获取用户,第二个参数未输出型参数
        bool SeidGetUsr(const std::string &sessionId, User &usr)
        {
            // 首先判断sessionId是否还有效
            if (!SessionIdIsValib(sessionId))
                return false;
            auto it = seid_usr.find(sessionId);
            if (it == seid_usr.end())
            {
                return false;
            }
            usr = it->second;
            return true;
        }
        // 使用sessionId获取Session,第二个参数未输出型参数
        bool SeidGetSess(const std::string &sessionId, Session &session)
        {
            auto it = seid_sess.find(sessionId);
            if (it == seid_sess.end())
            {
                return false;
            }
            session = it->second;
            return true;
        }
        // 使用idname获取用户,第二个参数未输出型参数
        bool IdGetUser(const std::string &idname, User &usr)
        {
            auto it = id_user.find(idname);
            if (it == id_user.end())
            {
                // 没有该用户名
                LOG(DEBUG) << "没有该用户名" << "\n";
                return false;
            }
            usr = it->second;
            return true;
        }
        // seeion是否有效
        bool SessionIdIsValib(const std::string &sessionId)
        {
            Session session;
            if(!SeidGetSess(sessionId, session))
            {
                return false;
            }
            if (time(nullptr) > session.end)
            {
                return false;
            }
            return true;
        }
        /*******************************
         * 用户注册
         * 1. idname:用户名 AND 辨别用户唯一性标识
         * 2. password:用户输入的密码
         * 3. email:用户邮箱
         ******************************/
        bool RegistUsr(const std::string &idname, const std::string &password, const std::string email)
        {
            std::string value = "'";
            value += idname;
            value += "','";
            value += password;
            value += "','";
            value += email;
            value += "'";
            if (!_manage_sql.InsertSql(value))
            {
                LOG(DEBUG) << "插入失败" << "\n";
                return false;
            }
            // 添加用户到用户管理
            id_user[idname] = User(idname, password, email);
            return true;
        }
        /*******************************
         * 用户登录
         * 1. idname:sql查找的key
         * 2. password:用户输入的密码
         * 3. sessionId:输入输出型参数返回的
         ******************************/
        bool LoginUsr(const std::string &idname, const std::string &password, std::string &sessionId)
        {
            std::vector<std::string> ret;
            if (!_manage_sql.SelectHaveKeySql(idname, &ret, "password"))
            {
                // LOG(DEBUG) << "没有数据行" << "\n";
                return false;
            }
            if (ret[0] == password)
            {
                // 账户密码正确
                // 创建session,并管理
                Session session;
                User usr;
                // 获取到usr
                if (!IdGetUser(idname, usr))
                {
                    LOG(FAIL) << "未知错误，无法找到用户" << "\n";
                    return false;
                }
                sessionId = session.sessionId;
                seid_sess[session.sessionId] = session;
                seid_usr[session.sessionId] = usr;

                return true;
            }
            return false;
        }
    };
    ManageUser *ManageUser::_manUsr = nullptr;

} // namespace ns_usr结束
